package cn.shop.merchant.controller.gametemplate;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.shop.merchant.model.gameconfig.GamePartition;
import cn.shop.merchant.model.gameconfig.GatewaySecret;
import cn.shop.merchant.model.gameconfig.Npc;
import cn.shop.merchant.req.GamePartitionQueryReq;
import cn.shop.merchant.service.GamePartitionService;
import cn.shop.merchant.service.GatewaySecretService;
import cn.shop.merchant.service.NpcService;
import cn.shop.merchant.service.OrderService;
import cn.shop.transcation.order.entity.Order;
import cn.shop.utils.ApiCode;
import cn.shop.utils.ApiResult;
import cn.shop.utils.Putif;
import cn.shop.utils.annotation.OperationLog;
import cn.shop.utils.enums.OperationType;
import cn.shop.utils.enums.OrderStatus;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import static cn.shop.utils.constant.Constant.UID;

/**
 * 游戏模板配置
 *
 * @author : 李一涵
 * @date : Created in 2021-11-5 9:29
 * @description : 游戏分区controller
 */
@RestController
@RequestMapping("/game/partition")
public class GamePartitionController {

    private static final Logger LOG = LoggerFactory.getLogger(GamePartitionController.class);

    @Resource
    private GamePartitionService partitionService;
    @Resource
    private NpcService npcService;
    @Resource
    private ObjectMapper objectMapper;
    @Resource
    private OrderService orderService;
    @Resource
    private GatewaySecretService secretService;

    private final static String MESSAGE = "{}同步至网关失败（通过填写的网关ip : {} 和 网关端口 : {} ,未连接到对应的网关。请检查网关是否运行或者检查网关的IP和端口是否与填写内容一致）";
    private final static String MESSAGE2 = "{}同步至网关失败（通过填写的网关ip : {} 和 网关端口 : {} ,{}）";

    /**
     * 获取游戏分区列表
     *
     * @param req 查询条件
     * @return 返回结果
     */
    @GetMapping("/list")
    public ApiResult<Map<String, Object>> list(GamePartitionQueryReq req) throws Exception {
        long uid = StpUtil.getSession().getLong(UID);
        Map<String, Object> data = partitionService.findConfigList(uid, req);
        return ApiResult.success(data);
    }

    /**
     * 新增游戏分区
     *
     * @param partition 游戏分区内容
     * @return 返回结果
     */
    @PostMapping("")
    @OperationLog(desc = "新增游戏分区", type = OperationType.CREATE)
    public ApiResult<Map<String, Object>> insert(@RequestBody GamePartition partition) {
        long uid = StpUtil.getSession().getLong(UID);
        if (partition == null) {
            return ApiResult.fail("模板信息不能为空");
        }
        String folder = partition.getServerFolder();
        boolean hasChar = folder.charAt(folder.length() - 1) == '\\';
        while (hasChar) {
            folder = folder.substring(0, folder.length() - 1);
            hasChar = folder.charAt(folder.length() - 1) == '\\';
        }
        partition.setServerFolder(folder);
        boolean isSuccess = partitionService.insertGamePartition(partition);
        if (isSuccess) {
            try {
                ApiResult<Map<String, Object>> result = sendPartition(partition, uid);
                if (result.getCode() == ApiCode.SUCCESS_SEND_FAIL) {
                    String msg = StrUtil.format(MESSAGE2, "分区创建成功，但", partition.getIp(), partition.getPort(), result.getMessage());
                    result.setMessage(msg);
                }
                return result;
            } catch (Exception e) {
                LOG.warn("同步分区错误", e);
                return ApiResult.fail(ApiCode.SUCCESS_SEND_FAIL, StrUtil.format(MESSAGE, "分区创建成功，但", partition.getIp(), partition.getPort()));
            }

        } else {
            return ApiResult.fail();
        }
    }

    /**
     * 修改游戏分区
     *
     * @param partition 游戏分区内容
     * @return 返回结果
     */
    @PutMapping("")
    @OperationLog(desc = "修改游戏分区", type = OperationType.UPDATE)
    public ApiResult<Map<String, Object>> update(@RequestBody GamePartition partition) {
        if (partition == null) {
            return ApiResult.fail("模板信息不能为空");
        }
        long uid = StpUtil.getSession().getLong(UID);
        String folder = partition.getServerFolder();
        boolean hasChar = folder.charAt(folder.length() - 1) == '\\';
        while (hasChar) {
            folder = folder.substring(0, folder.length() - 1);
            hasChar = folder.charAt(folder.length() - 1) == '\\';
        }
        partition.setServerFolder(folder);
        boolean isSuccess = partitionService.updateGameTemplate(partition, uid);
        if (isSuccess) {
            try {
                ApiResult<Map<String, Object>> result = sendPartition(partition, uid);
                if (result.getCode() == ApiCode.SUCCESS_SEND_FAIL) {
                    result.setMessage(StrUtil.format(MESSAGE2, "分区修改成功，但", partition.getIp(), partition.getPort(), result.getMessage()));
                }
                return result;
            } catch (Exception e) {
                LOG.warn("同步分区错误", e);
                return ApiResult.fail(ApiCode.SUCCESS_SEND_FAIL, StrUtil.format(MESSAGE, "分区修改成功，但", partition.getIp(), partition.getPort()));
            }
        } else {
            return ApiResult.fail();
        }
    }

    /**
     * 获取游戏分区信息
     *
     * @param id 分区编号
     * @return 返回结果
     */
    @GetMapping("/edit/{id}")
    public ApiResult<Map<String, Object>> edit(@PathVariable("id") long id) {
        if (id == 0) {
            return ApiResult.fail("参数错误");
        }
        long uid = StpUtil.getSession().getLong(UID);
        if (uid == 0) {
            return ApiResult.fail("用户编号为空，请检查登录状态");
        }
        Map<String, Object> data = partitionService.getInfo(id, uid);
        return ApiResult.success(data);
    }

    @GetMapping("/copy/{id}")
    public ApiResult<Map<String, Object>> copy(@PathVariable("id") long id) {
        if (id == 0) {
            return ApiResult.success();
        }
        long uid = StpUtil.getSession().getLong(UID);
        if (uid == 0) {
            return ApiResult.fail("用户编号为空，请检查登录状态");
        }
        Map<String, Object> data = partitionService.getInfo(id, uid);
        data.remove("id");
        return ApiResult.success(data);
    }

    /**
     * 修改显示顺序
     *
     * @param partition
     * @return 返回结果
     */
    @PutMapping("/setlevel")
    public ApiResult<Map<String, Object>> setLevel(@RequestBody GamePartition partition) {
        if (partition.getLevel() == null) {
            return ApiResult.fail("请设置顺序");
        }
        if (partition.getId() == null) {
            return ApiResult.fail("编号不能为空");
        }
        GamePartition update = new GamePartition();
        update.setLevel(partition.getLevel());
        update.setId(partition.getId());
        return ApiResult.result(partitionService.updateById(update));
    }

    /**
     * 删除分区信息
     *
     * @param id 分区编号
     * @return 返回结果
     */
    @DeleteMapping("/delete/{id}")
    @OperationLog(desc = "删除游戏分区", type = OperationType.DELETE)
    public ApiResult<Map<String, Object>> delete(@PathVariable("id") long id) {
        long uid = StpUtil.getSession().getLong(UID);
        if (uid == 0) {
            return ApiResult.fail("用户编号为空，请检查登录状态");
        }
        GamePartition partition = partitionService.getById(id);
        if (partition == null) {
            return ApiResult.fail();
        }
        long orderCount = orderService.lambdaQuery()
                .eq(Order::getPartitionId, id)
                .in(Order::getStatus, Arrays.asList(OrderStatus.AUDITING, OrderStatus.PASSED))
                .count();
        if (orderCount > 0) {
            return ApiResult.fail("请先转移未处理订单");
        }
        boolean isSuccess = partitionService.delete(id, uid);
        if (isSuccess) {
            try {
                partitionService.deleteSendToGateway(partition, uid);
            } catch (Exception e) {
                LOG.warn("删除分区通讯失败", e);
                //return ApiResult.fail(ApiCode.SUCCESS_SEND_FAIL, "通讯失败");
            }
            return ApiResult.success();
        } else {
            return ApiResult.fail();
        }
    }

    /**
     * 转移订单数据
     *
     * @param to 目标分区
     * @param from 原分区
     * @return 返回结果
     */
    @PutMapping("/transfer/{to}")
    @OperationLog(desc = "转移订单", type = OperationType.UPDATE)
    public ApiResult<Map<String, Object>> transfer(@PathVariable long to, @RequestBody List<Long> from) {
        if (partitionService.getById(to) == null) {
            return ApiResult.fail("目标分区不存在");
        }
        long count = orderService.count(Wrappers.<Order>lambdaQuery().in(Order::getPartitionId, from)
                .in(Order::getStatus, Arrays.asList(OrderStatus.AUDITING, OrderStatus.PASSED)));
        if (count == 0) {
            return ApiResult.fail("原分区无未处理订单数据");
        }
        return ApiResult.result(partitionService.transfer(from, to));
    }

    /**
     * 同步游戏分区信息到网关
     *
     * @param id 分区编号
     * @return 返回结果
     */
    @GetMapping("/send/{id}")
    @OperationLog(desc = "同步游戏分区信息", type = OperationType.UPLOAD)
    public ApiResult<Map<String, Object>> sendToGateway(@PathVariable("id") long id) {
        long uid = StpUtil.getSession().getLong(UID);
        List<Npc> npcs = npcService.list(Wrappers.<Npc>lambdaQuery().eq(Npc::getPartitionId, id));
        GamePartition partition = partitionService.getById(id);
        partition.setNpcs(npcs);
        try {
            ApiResult<Map<String, Object>> result = sendPartition(partition, uid);
            if (result.getCode() == ApiCode.SUCCESS_SEND_FAIL) {
                result.setMessage(StrUtil.format(MESSAGE2, "", partition.getIp(), partition.getPort(), result.getMessage()));
            }
            return result;
        } catch (Exception e) {
            LOG.warn("同步分区错误", e);
            return ApiResult.fail(ApiCode.SUCCESS_SEND_FAIL, StrUtil.format(MESSAGE, "", partition.getIp(), partition.getPort()));
        }
    }

    /**
     * 检测网关通信
     *
     * @param id 分区编号
     * @return
     */
    @GetMapping("/ping/{id}")
    public ApiResult<Map<String, Object>> ping(@PathVariable("id") long id) {
        GamePartition partition = partitionService.getById(id);
        GatewaySecret secret = secretService.getByIp(partition.getUid(), partition.getIp());
        ApiResult<Map<String, Object>> result = ApiResult.fail("通讯失败");
        try {
            String json = HttpUtil.createGet("http://" + partition.getIp() +
                            ":" + partition.getPort() + "/gateway/api/ping")
                    .header("secret", secret != null ? secret.getSecret() : "")
                    .execute()
                    .body();
            if (StrUtil.isBlank(json)) {
                return ApiResult.fail("秘钥无效");
            }
            objectMapper.readValue(json , new TypeReference<ApiResult<Object>>() {});
            return ApiResult.success("通讯成功");
        } catch (Exception e) {
            LOG.warn("通讯失败", e);
        }
        return result;
    }

    private ApiResult<Map<String, Object>> sendPartition(GamePartition partition, long uid) throws Exception {
        GatewaySecret secret = secretService.getByIp(uid, partition.getIp());
        String alias = partitionService.getAliasByGameId(partition.getGameId());
        Map<String, Object> body = Putif.start()
                .anyway("id", partition.getId())
                .anyway("templateId", partition.getTemplateId())
                .anyway("name", partition.getName())
                .anyway("serverFolder", partition.getServerFolder())
                .anyway("npcs", partition.getNpcs())
                .anyway("createScript", partition.getCreateScript())
                .anyway("alias", alias)
                .get();
        LOG.info("发送分区信息：{} {} {}", partition.getIp(), partition.getPort(), partition);
        String url = "http://" + partition.getIp() + ":" + partition.getPort() + "/gateway/api/partition/add";
        String json = HttpUtil.createPost(url)
                .timeout(8000)
                .header("secret", secret != null ? secret.getSecret() : "")
                .body(objectMapper.writeValueAsString(body))
                .execute()
                .body();
        LOG.info("同步分区返回结果:" + json);
        if (StrUtil.isBlank(json)) {
            return ApiResult.fail("秘钥无效");
        }
        return objectMapper.readValue(json, new TypeReference<ApiResult<Map<String, Object>>>() {});
    }
}
